/**
 * Copyright (c) 2012 Ibrahim Abd Elkader 
 * Email: <i.abdalkader@gmail.com> 
 * Licensed under the MIT license.
 * See the file COPYING for copying permission.
 */
#include <stdlib.h>
#include <ucontext.h>
#include <sys/time.h>
#include "sched.h"
#include "utask.h"
/* main context */
extern ucontext_t uctx_main;
/* task hash table */
static struct task_t *task_hash=NULL;
/* current task */
static struct task_t *current_task=NULL;

static void sched_yield();

static void sched_tick()
{
    // do nothing
    sched_yield(current_task);
}

static void sched_run()
{
    struct task_t *task;
    while (HASH_COUNT(task_hash) > 0) {
        for(task=task_hash; task; task=task->hh.next) {
            /* set current task */
            current_task = task;
            /* get current time in us*/
            long tick = utask_time_us();
            /* run task */
            swapcontext(&uctx_main, &task->uctx);
            /* add to the task running time */
            task->ticks += utask_time_us()-tick;
            /* check if the task should be removed */
            if (task->remove) {
                /* remove from hash */
                HASH_DEL(task_hash, task);
                /* free task struct */
                utask_free(task);
            }
        }
    }
}

static void sched_create(struct task_t *task)
{
    /* insert task into hash table */
    HASH_ADD_INT(task_hash, id, task); 
}

static void sched_destroy()
{
    /* mark task for removal when it yields
       this is nesscary as we still need to 
       do some time management for the task */
    current_task->remove = 1;        
}

static void sched_yield()
{    
    /* switch to the main context, execution 
       will resume at continue at sched_run */
    swapcontext(&current_task->uctx, &uctx_main);
}

static void sched_debug()
{
    /* add debugging code */
}

struct sched_t *sched_rr_alloc()
{
    struct sched_t *s;
    s = (struct sched_t*) malloc(sizeof(struct sched_t));
    s->run = sched_run;
    s->tick= sched_tick;
    s->create= sched_create;
    s->destroy= sched_destroy;
    s->yield= sched_yield;
    s->debug= sched_debug;
    return s;
}

static void sched_free(struct sched_t *sched)
{
    free(sched);
}
